<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="generator" content="rustdoc">
    <title>Your First Bluetooth Low Energy App with Flutter</title>

    
    <!-- Begin scripts/articles/*-header.html: Article Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<meta property="og:title" 
    content="Your First Bluetooth Low Energy App with Flutter" 
    data-rh="true">
<meta property="og:description" 
    content="Bluetooth Low Energy apps are ridiculously easy to code with Flutter and Dart, let me show you how!" 
    data-rh="true">
<meta property="og:image" 
    content="https://lupyuen.github.io/images/flutter-title.png">
<meta property="og:type" 
    content="article" data-rh="true">
<!-- End scripts/articles/*-header.html -->
<!-- Begin scripts/rustdoc-header.html: Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<link rel="alternate" type="application/rss+xml" title="RSS Feed for lupyuen" href="/rss.xml" />
<link rel="stylesheet" type="text/css" href="../normalize.css">
<link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle">
<link rel="stylesheet" type="text/css" href="../dark.css">
<link rel="stylesheet" type="text/css" href="../light.css" id="themeStyle">
<script src="../storage.js"></script><noscript>
<link rel="stylesheet" href="../noscript.css"></noscript>
<link rel="shortcut icon" href="../favicon.ico">
<style type="text/css">
    #crate-search {
        background-image: url("../down-arrow.svg");
    }
    a {
        color: #77d;
    }
</style>
<!-- End scripts/rustdoc-header.html -->


</head>
<body class="rustdoc">
    <!--[if lte IE 8]>
    <div class="warning">
        This old browser is unsupported and will most likely display funky
        things.
    </div>
    <![endif]-->

        <!-- Begin scripts/rustdoc-before.html: Pre-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->

    <!-- Begin Theme Picker -->
    <div class="theme-picker" style="left: 0"><button id="theme-picker" aria-label="Pick another theme!"><img src="../brush.svg"
        width="18" alt="Pick another theme!"></button>
        <div id="theme-choices"></div>
    </div>
    <script src="../theme.js"></script>
    <!-- Theme Picker -->

    <!-- End scripts/rustdoc-before.html -->
    

    <h1 class="title">Your First Bluetooth Low Energy App with Flutter</h1>
    <nav id="TOC"><ul>
<li><a href="#download-flutter-sdk">1 Download Flutter SDK</a><ul></ul></li>
<li><a href="#install-flutter-tools">2 Install Flutter Tools</a><ul></ul></li>
<li><a href="#flutter-for-android">3 Flutter for Android</a><ul></ul></li>
<li><a href="#flutter-for-ios">4 Flutter for iOS</a><ul></ul></li>
<li><a href="#download-source-code-for-flutter-app">5 Download Source Code for Flutter App</a><ul></ul></li>
<li><a href="#debug-flutter-app">6 Debug Flutter App</a><ul></ul></li>
<li><a href="#sign-flutter-app-for-ios">7 Sign Flutter App for iOS</a><ul></ul></li>
<li><a href="#bluetooth-le-services">8 Bluetooth LE Services</a><ul></ul></li>
<li><a href="#bluetooth-le-code">9 Bluetooth LE Code</a><ul></ul></li>
<li><a href="#whats-next">10 What's Next</a><ul></ul></li>
<li><a href="#further-reading">11 Further Reading</a><ul></ul></li></ul></nav><p><img src="https://lupyuen.github.io/images/flutter-title.png" alt="Flutter App with Bluetooth Low Energy running on a real Android phone, connected to VSCode Debugger" /></p>
<p><em>Flutter App with Bluetooth Low Energy running on a real Android phone, connected to VSCode Debugger</em></p>
<p>Ready to create your very first <em>&quot;Hello World&quot;</em> app with <a href="https://flutter.dev/"><strong>Flutter</strong></a>?</p>
<p>Why not make a sophisticated app that says...</p>
<p><em>&quot;Hello Bluetooth Low Energy gadgets nearby... Tell me what's inside you!&quot;</em></p>
<p>With Flutter, Bluetooth LE (Low Energy) apps for Android AND iOS are ridiculously easy to build, let me show you how!</p>
<h1 id="download-flutter-sdk" class="section-header"><a href="#download-flutter-sdk">1 Download Flutter SDK</a></h1>
<p>The Flutter SDK works on Windows, macOS and Linux (Intel, not Arm, <a href="https://twitter.com/MisterTechBlog/status/1267755390814453760?s=20">so Raspberry Pi is no-go</a>).</p>
<ol>
<li>
<p><a href="https://flutter.dev/docs/get-started/install">Download the Flutter SDK</a></p>
</li>
<li>
<p>Unzip the Flutter SDK to our Home Directory. </p>
</li>
<li>
<p>Add <code>flutter/bin</code> to our PATH.</p>
<p>For macOS and Linux, we may edit <code>~/.bashrc</code> (or equivalent) and add this...</p>
<pre><code class="language-bash">export PATH=&quot;$PATH:$HOME/flutter/bin&quot;
</code></pre>
</li>
<li>
<p>Open a new Command Prompt. Check the Flutter SDK by entering...</p>
<pre><code class="language-bash">flutter
</code></pre>
<p>We should see this helpful message...</p>
</li>
</ol>
<p><img src="https://lupyuen.github.io/images/flutter-doctor1.png" alt="Flutter Tool" /></p>
<h1 id="install-flutter-tools" class="section-header"><a href="#install-flutter-tools">2 Install Flutter Tools</a></h1>
<ol>
<li>
<p><a href="https://code.visualstudio.com/">Download and install VSCode</a></p>
</li>
<li>
<p>At the Command Prompt, enter...</p>
<pre><code class="language-bash">flutter doctor
</code></pre>
<p>We will see something like this...</p>
<p><img src="https://lupyuen.github.io/images/flutter-doctor2.png" alt="Flutter Doctor" /></p>
</li>
<li>
<p>Whoa that's a long list of complaints! But we shall fix only 3 things: <strong>Android Toolchain</strong> (or <strong>Xcode for iOS</strong>), <strong>VSCode</strong> and <strong>Connected Device</strong></p>
</li>
</ol>
<p>Let's fix them now...</p>
<h1 id="flutter-for-android" class="section-header"><a href="#flutter-for-android">3 Flutter for Android</a></h1>
<p><em>(If you're building for iPhone, skip to the next section)</em></p>
<ol>
<li>
<p><strong>Android Toolchain</strong>: Follow the instructions shown in your screen. </p>
<p>You may need to run <code>sdkmanager</code> and <code>flutter doctor --android-licenses</code></p>
</li>
<li>
<p><strong>VSCode</strong>: Launch VSCode. Click <code>View → Extensions</code></p>
<p>Install the Flutter Extension for VSCode...</p>
<p><img src="https://lupyuen.github.io/images/flutter-vscode.png" alt="Flutter Extension for VSCode" /></p>
</li>
<li>
<p><strong>Connected Device</strong>: Connect our Android phone (with debugging enabled) to the USB port...</p>
<p><img src="https://lupyuen.github.io/images/flutter-usb.jpg" alt="Connect phone to USB port" /></p>
</li>
<li>
<p>After connecting our Android phone, we should see the phone in the VSCode status bar...</p>
<p><img src="https://lupyuen.github.io/images/flutter-device.png" alt="Flutter Device in VSCode" /></p>
</li>
<li>
<p>At the Command Prompt, enter...</p>
<pre><code class="language-bash">flutter -v devices
</code></pre>
<p>We should see our phone...</p>

<div class="example-wrap"><pre class="rust rust-example-rendered">
<span class="ident">List</span> <span class="ident">of</span> <span class="ident">devices</span> <span class="ident">attached</span>
<span class="number">99031FFG</span> <span class="ident">device</span> <span class="ident">usb</span>:<span class="number">3376X</span> <span class="ident">product</span>:<span class="ident">coral</span> <span class="ident">model</span>:<span class="ident">Pixel_4_XL</span> <span class="ident">device</span>:<span class="ident">coral</span>
<span class="ident">Pixel</span> <span class="number">4</span> <span class="ident">XL</span> • <span class="number">99031FFG</span> • <span class="ident">android</span><span class="op">-</span><span class="ident">arm64</span> • <span class="ident">Android</span> <span class="number">10</span> (<span class="ident">API</span> <span class="number">29</span>)</pre></div>
</li>
<li>
<p>Finally enter...</p>
<pre><code class="language-bash">flutter doctor
</code></pre>
<p>We should see ticks for <strong>Flutter</strong>, <strong>Android Toolchain</strong>, <strong>VSCode</strong> and <strong>Connected Device</strong>...</p>
<p><img src="https://lupyuen.github.io/images/flutter-doctor3.png" alt="Flutter Doctor After Fixes" /></p>
</li>
<li>
<p>We may ignore the other issues for now</p>
</li>
</ol>
<h1 id="flutter-for-ios" class="section-header"><a href="#flutter-for-ios">4 Flutter for iOS</a></h1>
<p><em>(If you're building for Android, skip to the next section)</em></p>
<p>Let's fix 3 things shown below: <strong>Xcode</strong>, <strong>VSCode</strong> and <strong>Connected Device</strong></p>
<p><img src="https://lupyuen.github.io/images/flutter-doctor2.png" alt="Flutter Doctor" /></p>
<ol>
<li>
<p><strong>Xcode</strong>: Follow the instructions shown in your screen. </p>
<p>You may need to install Xcode, CocoaPods and run <code>xcodebuild</code></p>
</li>
<li>
<p><strong>VSCode</strong>: Launch VSCode. Click <code>View → Extensions</code></p>
<p>Install the Flutter Extension for VSCode...</p>
<p><img src="https://lupyuen.github.io/images/flutter-vscode.png" alt="Flutter Extension for VSCode" /></p>
</li>
<li>
<p><strong>Connected Device</strong>: Connect our iPhone to the USB port.</p>
</li>
<li>
<p>After connecting our iPhone, we should see the phone in the VSCode status bar...</p>
<p><img src="https://lupyuen.github.io/images/flutter-deviceios.png" alt="Flutter on iOS" /></p>
</li>
<li>
<p>At the Command Prompt, enter...</p>
<pre><code class="language-bash">flutter -v devices
</code></pre>
<p>We should see our phone...</p>

<div class="example-wrap"><pre class="rust rust-example-rendered">
<span class="number">1</span> <span class="ident">connected</span> <span class="ident">device</span>:
<span class="ident">iPhone</span> <span class="number">6</span> <span class="ident">Plus</span> • <span class="ident">ios</span> • <span class="ident">iOS</span> <span class="number">12.4</span>.<span class="number">6</span></pre></div>
</li>
<li>
<p>Finally enter...</p>
<pre><code class="language-bash">flutter doctor
</code></pre>
<p>We should see ticks for <strong>Flutter</strong>, <strong>Xcode</strong>, <strong>VSCode</strong> and <strong>Connected Device</strong>...</p>
<p><img src="https://lupyuen.github.io/images/flutter-doctorios.png" alt="Flutter Doctor After Fixes" /></p>
</li>
<li>
<p>We may ignore the other issues for now</p>
</li>
</ol>
<h1 id="download-source-code-for-flutter-app" class="section-header"><a href="#download-source-code-for-flutter-app">5 Download Source Code for Flutter App</a></h1>
<p>The source code for our Flutter app is located here...</p>
<p><a href="https://github.com/lupyuen/flutter-blue-sample"><code>github.com/lupyuen/flutter-blue-sample</code></a></p>
<p>Our app is derived from the <a href="https://github.com/pauldemarco/flutter_blue/tree/master/example">sample app</a> that comes with the <a href="https://github.com/pauldemarco/flutter_blue"><code>flutter_blue</code></a> Bluetooth LE plugin for Flutter.</p>
<ol>
<li>
<p>In VSCode, click <code>View → Command Palette</code></p>
</li>
<li>
<p>Enter <code>Git Clone</code></p>
</li>
<li>
<p>Enter <code>https://github.com/lupyuen/flutter-blue-sample</code></p>
</li>
<li>
<p>Select a folder to download the source code</p>
</li>
<li>
<p>When prompted to open the cloned repository, click <code>Open</code></p>
</li>
<li>
<p>When prompted to get missing packages, click <code>Get Packages</code></p>
</li>
</ol>
<p>Check this video for the steps to download the source code for our Flutter app...</p>
<ul>
<li>
<p><a href="https://youtu.be/QSrg9DgLwjk">Watch on YouTube</a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/pinetime-rust-mynewt/releases/download/v4.2.1/flutter-debug.mov">Download the video</a></p>
</li>
</ul>
<h1 id="debug-flutter-app" class="section-header"><a href="#debug-flutter-app">6 Debug Flutter App</a></h1>
<p>We're now ready to debug our Flutter app on a real Android or iOS phone!</p>
<ol>
<li>
<p>In VSCode, click <code>Run → Start Debugging</code></p>
<p><img src="https://lupyuen.github.io/images/flutter-debug1.png" alt="Start Debugging in VSCode" /></p>
</li>
<li>
<p>Select the <code>Dart &amp; Flutter</code> debugger</p>
<p><img src="https://lupyuen.github.io/images/flutter-debug2.png" alt="Select Debugger in VSCode" /></p>
</li>
<li>
<p>Wait for the Flutter app to be compiled and deployed to our phone (May take a minute for the first time)</p>
</li>
<li>
<p>For iOS: Check the next section for additional Xcode steps</p>
</li>
<li>
<p>When the Flutter app starts, we'll be able to Scan, Connect, Reload and Expand devices over Bluetooth LE like this...</p>
</li>
</ol>
<p><img src="https://lupyuen.github.io/images/flutter-scan.png" alt="Scanning for Bluetooth LE devices" /></p>
<p>And that's our very first Flutter app with Bluetooth LE!</p>
<p>VSCode Debugger has many useful features for debugging Flutter apps. Here's what we see when we hit an unhandled exception in our Flutter app...</p>
<p><img src="https://lupyuen.github.io/images/flutter-debug.png" alt="Flutter App with VSCode Debugger" /></p>
<p><a href="https://lupyuen.github.io/images/flutter-debug.png"><em>Larger image</em></a></p>
<ul>
<li>
<p><strong>Dev Tools</strong>: Shows the widgets rendered in our app</p>
</li>
<li>
<p><strong>Variables</strong>: Shows the local and global variables and their values</p>
</li>
<li>
<p><strong>Call Stack</strong>: Function calls leading to the exception or breakpoint</p>
</li>
<li>
<p><strong>Debug Console</strong>: Compilation, deployment and runtime messages</p>
</li>
<li>
<p><strong>Source Code</strong>: Shows the line of code for the exception or breakpoint</p>
</li>
<li>
<p><strong>Debug Toolbar</strong>: Resume execution, step into functions, step over code, hot reload, restart execution... <a href="https://code.visualstudio.com/docs/editor/debugging">More about debugging</a></p>
</li>
</ul>
<p>See this article for more details on building Flutter apps with VSCode, including cool features like Hot Reload...</p>
<ul>
<li><a href="https://flutter.dev/docs/development/tools/vs-code">Flutter Development on VSCode</a></li>
</ul>
<p>Here's a video of the steps for debugging a Flutter app with VSCode...</p>
<ul>
<li>
<p><a href="https://youtu.be/QSrg9DgLwjk">Watch on YouTube</a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/pinetime-rust-mynewt/releases/download/v4.2.1/flutter-debug.mov">Download the video</a></p>
</li>
</ul>
<h1 id="sign-flutter-app-for-ios" class="section-header"><a href="#sign-flutter-app-for-ios">7 Sign Flutter App for iOS</a></h1>
<p><em>(Skip this section if you're building for Android)</em></p>
<p>This message appears when we debug our iOS app...</p>
<p><img src="https://lupyuen.github.io/images/flutter-sign.png" alt="Xcode Signing" /></p>
<p>Here's what we need to do for iOS...</p>
<ol>
<li>
<p>In VSCode, click <code>Terminal → New Terminal</code></p>
</li>
<li>
<p>At the Terminal prompt, enter...</p>
<pre><code class="language-bash">open ios/Runner.xcworkspace
</code></pre>
<p><img src="https://lupyuen.github.io/images/flutter-workspace.png" alt="Open Xcode workspace" /></p>
</li>
<li>
<p>In Xcode, click <code>Runner → Targets Runner → Signing &amp; Capabilities</code></p>
<p><img src="https://lupyuen.github.io/images/flutter-sign2.png" alt="Xcode Signing" /></p>
</li>
<li>
<p>Set <code>Team</code> to our Apple Developer Account</p>
</li>
<li>
<p>Set <code>Bundle Identifier</code> to a unique name</p>
</li>
<li>
<p>On our iPhone, click <code>Settings → General → Device Management</code></p>
</li>
<li>
<p>Set the Trust Settings like this...</p>
<p><img src="https://lupyuen.github.io/images/flutter-trust.png" alt="Trust iOS Developer" /></p>
</li>
</ol>
<p>We should be able to launch and debug our Flutter app using the instructions from the previous section...</p>
<p><img src="https://lupyuen.github.io/images/flutter-ios.jpg" alt="Flutter App on iOS" /></p>
<p>Here's a demo of our Flutter app on iPhone...</p>
<ul>
<li>
<p><a href="https://youtu.be/MTBEd8xRrpA">Watch on YouTube</a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/pinetime-rust-mynewt/releases/download/v4.2.1/flutter-ios.mov">Download the video</a></p>
</li>
</ul>
<p><img src="https://lupyuen.github.io/images/micropython-title.jpg" alt="PineTime Smart Watch" /></p>
<p><em>PineTime Smart Watch</em></p>
<h1 id="bluetooth-le-services" class="section-header"><a href="#bluetooth-le-services">8 Bluetooth LE Services</a></h1>
<p>Let's connect our Flutter app to a <a href="https://wiki.pine64.org/index.php/PineTime"><strong>PineTime Smart Watch</strong></a>...</p>
<ul>
<li>
<p><a href="https://youtu.be/pt-BYs_7qOE">Watch on YouTube</a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/pinetime-rust-mynewt/releases/download/v4.2.1/flutter-pinetime-rotated.mp4">Download the video</a></p>
</li>
</ul>
<p><em>So many Services and Characteristics... What are they?</em></p>
<p>When we access data and perform functions wirelessly on a Bluetooth LE device (like PineTime), we talk via a Bluetooth LE protocol known as the <strong>Generic Attribute (GATT) Profile</strong>. </p>
<p>GATT defines the standard way for a Bluetooth LE Client (like our Flutter app) to access a Bluetooth LE Service (like on the PineTime Smart Watch).  <a href="https://learn.adafruit.com/introduction-to-bluetooth-low-energy/gatt">More about GATT</a></p>
<p>Our Flutter app displays the GATT Services and GATT Characteristics supported by the Bluetooth LE device. Let's look at the <a href="https://www.bluetooth.com/specifications/gatt/services/"><strong>Standard GATT Services</strong></a> that are defined in the Bluetooth LE Specifications...</p>
<p><img src="https://lupyuen.github.io/images/flutter-services.png" alt="Bluetooth LE Services on PineTime Smart Watch" /></p>
<ol>
<li>
<p><strong>Generic Access</strong> (<code>0x1800</code>):
This GATT Service contains two GATT Charactertistics: Device Name (<code>pinetime</code>) and Appearance. <a href="https://www.bluetooth.com/xml-viewer/?src=https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.generic_access.xml">Specifications</a></p>
</li>
<li>
<p><strong>Generic Attribute</strong> (<code>0x1801</code>): This GATT Services notifies our Flutter app of any changes in PineTime's GATT Services.
<a href="https://www.bluetooth.com/xml-viewer/?src=https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.generic_attribute.xml">Specifications</a></p>
</li>
<li>
<p><strong>Device Information</strong> (<code>0x180A</code>): Contains two GATT Charactertistics: Model Number (<code>Apache Mynewt NimBLE</code>) and Firmware Revision (<code>1.0.0</code>).
<a href="https://www.bluetooth.com/xml-viewer/?src=https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.device_information.xml">Specifications</a></p>
</li>
<li>
<p><strong>Alert Notification Service</strong> (<code>0x1811</code>): For Alerts and Notifications.
<a href="https://www.bluetooth.com/xml-viewer/?src=https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.alert_notification.xml">Specifications</a></p>
</li>
</ol>
<p>Some GATT Characteristics are shown as a list of numbers...</p>

<div class="example-wrap"><pre class="rust rust-example-rendered">
    <span class="ident">Device</span> <span class="ident">Name</span>:
    <span class="ident">Characteristic</span> <span class="number">0x2A00</span>
    [<span class="number">112</span>, <span class="number">105</span>, <span class="number">110</span>, ...]</pre></div>
<p>The numbers are the ASCII codes for the text strings. We can see the actual strings in the Nordic nRF Connect app below.</p>
<p><em>Can we do complex things with GATT? Like update the firmware wirelessly on PineTime?</em></p>
<p>There's a custom GATT Service and Characteristic for that!</p>
<p><strong>Simple Management Protocol</strong> (<code>8D53DC1D-1DB7-4CD3-868B-8A527460AA84</code>, shortened to <code>0xDC1D</code> above) is the GATT Service used by PineTime for updating the firmware.</p>
<p>The GATT Service contains a single GATT Characteristic (<code>DA2E7828-FBCE-4E01-AE9E-261174997C48</code>, shortened to <code>0x7828</code> above). Our Flutter app may someday update PineTime's firmware by sending a Write Request to this GATT Characteristic (with the firmware file encoded in the request).</p>
<p><a href="https://lupyuen.github.io/pinetime-rust-mynewt/articles/dfu">More about Wireless Firmware Update on PineTime</a></p>
<p>The final GATT Service (<code>59462f12-9543-9999-12c8-58b459a2712d</code>, shorted to <code>0x2F12</code> above) is the <strong>Security Test Service</strong>. <a href="https://github.com/apache/mynewt-nimble/blob/master/apps/btshell/src/gatt_svr.c#L67-L94">More details</a></p>
<p>For comparison, here are the GATT Services that appear when the <a href="https://www.nordicsemi.com/Software-and-tools/Development-Tools/nRF-Connect-for-mobile">Nordic nRF Connect</a> mobile app is connected to PineTime. So our Flutter app really works!</p>
<p><img src="https://lupyuen.github.io/images/dfu-gattservices.jpg" alt="Nordic nRF Connect app connected to PineTime" /></p>
<h1 id="bluetooth-le-code" class="section-header"><a href="#bluetooth-le-code">9 Bluetooth LE Code</a></h1>
<p>I'm new to Flutter and Dart... And I find it absolutely amazing that a few lines of code can do so much!</p>
<p>Our app is structured like this to scan Bluetooth LE devices and display them...</p>
<p><img src="https://lupyuen.github.io/images/flutter-structure.png" alt="Our App Structure" /></p>
<p>Here's the code that implements the screen for scanning Bluetooth LE devices: <a href="https://github.com/lupyuen/flutter-blue-sample/blob/master/lib/main.dart#L67-L153"><code>lib/main.dart</code></a></p>
<pre><code class="language-dart">//  Screen for finding devices
class FindDevicesScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      //  Title for the screen
      appBar: AppBar(
        title: Text('Find Devices'),
      ),

      body: RefreshIndicator(
        //  Start scanning for Bluetooth LE devices
        onRefresh: () =&gt;
            FlutterBlue.instance.startScan(timeout: Duration(seconds: 4)),

        //  List of Bluetooth LE devices
        child: SingleChildScrollView(
          child: Column(
            children: &lt;Widget&gt;[
              ...
              StreamBuilder&lt;List&lt;ScanResult&gt;&gt;(
                stream: FlutterBlue.instance.scanResults,
                initialData: [],

                builder: (c, snapshot) =&gt; Column(
                  children: snapshot.data
                      .map(
                        //  For each Bluetooth LE device, show the ScanResultTile widget when tapped
                        (r) =&gt; ScanResultTile(
                          result: r,
                          onTap: () =&gt; Navigator.of(context)
                              .push(MaterialPageRoute(builder: (context) {
                            r.device.connect();
                            return DeviceScreen(device: r.device);
                          })),
                        ),
                      )
                      .toList(),
                ),
              ),
            ...
</code></pre>
<p>And here's the code that renders each Bluetooth LE device found: <a href="https://github.com/lupyuen/flutter-blue-sample/blob/master/lib/widgets.dart#L8-L121"><code>lib/widgets.dart</code></a></p>
<pre><code class="language-dart">//  Widget for displaying a Bluetooth LE device
class ScanResultTile extends StatelessWidget {
  ...
  @override
  Widget build(BuildContext context) {
    return ExpansionTile(
      //  Show the device name and signal strength
      title: _buildTitle(context),
      leading: Text(result.rssi.toString()),

      //  Show the Connect button and call onTap when tapped
      trailing: RaisedButton(
        child: Text('CONNECT'),
        color: Colors.black,
        textColor: Colors.white,
        onPressed: (result.advertisementData.connectable) ? onTap : null,
      ),

      //  Display the device's name, power level, manufacturer data, service UUIDs and service data
      children: &lt;Widget&gt;[
        _buildAdvRow(
            context, 'Complete Local Name', result.advertisementData.localName),
        _buildAdvRow(context, 'Tx Power Level',
            '${result.advertisementData.txPowerLevel ?? 'N/A'}'),
        _buildAdvRow(
            context,
            'Manufacturer Data',
            getNiceManufacturerData(
                result.advertisementData.manufacturerData) ??
                'N/A'),
        _buildAdvRow(
            context,
            'Service UUIDs',
            (result.advertisementData.serviceUuids.isNotEmpty)
                ? result.advertisementData.serviceUuids.join(', ').toUpperCase()
                : 'N/A'),
        _buildAdvRow(context, 'Service Data',
            getNiceServiceData(result.advertisementData.serviceData) ?? 'N/A'),
      ],
    );
  }
  ...
</code></pre>
<p>Yes the code looks similar to JavaScript. (Because Dart was designed to <a href="https://dart.dev/faq#q-why-isnt-dart-more-like-haskell--smalltalk--python--scala--other-language">compile to JavaScript efficiently</a>)</p>
<p>But overall the user interface code looks Declarative and Functional... A huge improvement over JavaScript and React Native!</p>
<p><em>How do we call the Bluetooth LE functions in our own Flutter app?</em></p>
<p>Just add the <a href="https://github.com/pauldemarco/flutter_blue"><code>flutter_blue</code></a> plugin as a dependency like this: <a href="https://github.com/lupyuen/flutter-blue-sample/blob/master/pubspec.yaml"><code>pubspec.yaml</code></a></p>

<div class="example-wrap"><pre class="rust rust-example-rendered">
<span class="ident">dependencies</span>:
  <span class="ident">flutter_blue</span>: <span class="op">^</span><span class="number">0.7</span>.<span class="number">2</span></pre></div>
<h1 id="whats-next" class="section-header"><a href="#whats-next">10 What's Next</a></h1>
<p>I'll be using the code in this article to create the open source <strong>PineTime Companion App</strong> for Android and iOS. So that we can flash our PineTime Smart Watches wirelessly, sync the date and time, show notifications, chart our heart rate, ...</p>
<p>Flutter makes it really easy to maintain a single code base for Android and iOS... And <code>flutter_blue</code> makes Bluetooth LE coding so simple!</p>
<p>If you're keen to help out, come chat with the PineTime FOSS Community (and me) in the PineTime Chatroom!</p>
<p><a href="https://wiki.pine64.org/index.php/PineTime#Community">PineTime Chatroom on Matrix / Discord / Telegram / IRC</a></p>
<h1 id="further-reading" class="section-header"><a href="#further-reading">11 Further Reading</a></h1>
<p><em><a href="https://lupyuen.github.io/pinetime-rust-mynewt/articles/mcuboot">&quot;MCUBoot Bootloader for PineTime Smart Watch (nRF52)&quot;</a></em></p>
<p><em><a href="https://lupyuen.github.io/pinetime-rust-mynewt/articles/dfu">&quot;Firmware Update over Bluetooth Low Energy on PineTime Smart Watch&quot;</a></em></p>
<p><em><a href="https://lupyuen.github.io/pinetime-rust-mynewt/articles/dfutest">&quot;Wireless Firmware Update In Action on PineTime Smart Watch (nRF52)&quot;</a></em></p>
<p><a href="https://github.com/lupyuen/pinetime-rust-mynewt/blob/master/README.md">Check out the other PineTime articles</a></p>
<p><a href="https://lupyuen.github.io/rss.xml">RSS Feed</a></p>

    
</body>
</html>